﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using SimpleRest.Core.Extensions.OAuth.Exceptions;
using SimpleRest.Core.Routing;

namespace SimpleRest.Core.Extensions.OAuth
{
    public class OAuthMessageHandler : IHttpMessageHandler
    {
        protected readonly IAuthorizationAuthority AuthorizationAuthority;

        /// <summary>
        /// Pass in an authorization authority to tell this message handler how to behave once it has determined a request has been made
        /// with valid oAuth credentials.  Anything inheriting AuthorizationServiceBase extends IAuthorizationAuthority so passing in
        /// a reference to this service can be done.
        /// </summary>
        /// <param name="authorizationAuthority"></param>
        public OAuthMessageHandler(IAuthorizationAuthority authorizationAuthority)
        {
            if (authorizationAuthority == null)
                throw new ArgumentNullException("authorizationAuthority");

            this.AuthorizationAuthority = authorizationAuthority;
        }

        public ExecuteResponseHandler Execute(MessageHandlerContext request)
        {
            ProtectedResourceAttribute attr = null;
            if (request.TargetOperation == null || request.TargetOperation.TargetMethod == null)
                return null; // not targeting a resource.

            attr = GetProtectedResourceAttribute(request.TargetOperation.TargetMethod);

            if (attr == null)
            {
                attr = GetProtectedResourceAttribute(request.TargetService);
                if (attr == null)
                    return null;
            }

            var header = request.Request.Headers.FirstOrDefault(d => d.Key.ToLower().Equals("authorization"));
            string val = null;
            if (header != null && !string.IsNullOrEmpty(header.Value))
                val = header.Value;

            if (val == null)
                throw new UnAuthorizedException();

            var valSplit = val.Split(new char[] { ' ' });
            if (valSplit.Length < 2)
                throw new InvalidRequestException();

            string tokenType = valSplit[0].Trim();

            val = "";
            for (int i = 1; i < valSplit.Length;i++ )
            {
                val += valSplit[i] + " ";
            }
            val = val.Trim();

            ServiceRoute targetServiceRoute = null;
            IAuthorizationAuthority authorizationHandler = null;

            bool isAuthenticated = false;

            switch (tokenType.ToLower())
            {
                case "bearer":
                    isAuthenticated = this.AuthorizationAuthority.IsAuthorized(request.Request, new BearerToken() { Value = val }, attr.ResourceKey, request.TargetOperation);
                    break;
                case "mac":
                    string id = "";
                    string nonce = "";
                    string mac = "";
                    string ts = "";
                    string ext = "";

                    foreach (var splitVal in val.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                    {
                        var kvpsplit = splitVal.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                        if (kvpsplit.Length > 0)
                        {
                            string key = kvpsplit[0].Trim();
                            string value = "";
                            if (kvpsplit.Length > 1)
                                value = kvpsplit[1].Trim(new char[] { ' ', '"' });
                            if (key.ToLower() == "id")
                                id = value;
                            if (key.ToLower() == "nonce")
                                nonce = value;
                            if (key.ToLower() == "mac")
                                mac = value;
                            if (key.ToLower() == "ext")
                                ext = value;
                            if (key.ToLower() == "ts")
                                ts = value;
                        }
                    }

                    isAuthenticated = this.AuthorizationAuthority.IsAuthorized(request.Request, new MacToken() { ext = ext, id = id, mac = mac, nonce = nonce, ts = ts }, attr.ResourceKey, request.TargetOperation);

                    break;
                default:
                    throw new InvalidRequestException();
                    break;
            }

            if (!isAuthenticated)
            {
                throw new UnAuthorizedException();
            }

            return null;
        }

        private ProtectedResourceAttribute GetProtectedResourceAttribute(Type type)
        {
            foreach (object attr in type.GetCustomAttributes(true))
                if (attr is ProtectedResourceAttribute)
                    return attr as ProtectedResourceAttribute;
            return null;
        }
        private ProtectedResourceAttribute GetProtectedResourceAttribute(MethodInfo method)
        {
            foreach (object attr in method.GetCustomAttributes(true))
                if (attr is ProtectedResourceAttribute)
                    return attr as ProtectedResourceAttribute;
            return null;
        }
    }
}
